retour\arch\x86\thunk/
x86.rs

1use crate::pic::{FixedThunk, Thunkable};
2use generic_array::{typenum, GenericArray};
3use std::mem;
4
5#[repr(packed)]
6pub struct JumpRel {
7  opcode: u8,
8  operand: u32,
9}
10
11/// Constructs either a relative jump or call.
12fn relative32(destination: usize, is_jump: bool) -> Box<dyn Thunkable> {
13  const CALL: u8 = 0xE8;
14  const JMP: u8 = 0xE9;
15
16  Box::new(FixedThunk::<typenum::U5>::new(move |source| {
17    let code = JumpRel {
18      opcode: if is_jump { JMP } else { CALL },
19      operand: calculate_displacement(source, destination, mem::size_of::<JumpRel>()),
20    };
21
22    let slice: [u8; 5] = unsafe { mem::transmute(code) };
23    GenericArray::clone_from_slice(&slice)
24  }))
25}
26
27/// Returns a no-op instruction.
28pub fn nop() -> Box<dyn Thunkable> {
29  Box::new([0x90].to_vec())
30}
31
32/// Constructs a relative call operation.
33pub fn call_rel32(destination: usize) -> Box<dyn Thunkable> {
34  relative32(destination, false)
35}
36
37/// Constructs a relative jump operation.
38pub fn jmp_rel32(destination: usize) -> Box<dyn Thunkable> {
39  relative32(destination, true)
40}
41
42#[repr(packed)]
43struct JccRel {
44  opcode0: u8,
45  opcode1: u8,
46  operand: u32,
47}
48
49/// Constructs a conditional relative jump operation.
50pub fn jcc_rel32(destination: usize, condition: u8) -> Box<dyn Thunkable> {
51  Box::new(FixedThunk::<typenum::U6>::new(move |source| {
52    let code = JccRel {
53      opcode0: 0x0F,
54      opcode1: 0x80 | condition,
55      operand: calculate_displacement(source, destination, mem::size_of::<JccRel>()),
56    };
57
58    let slice: [u8; 6] = unsafe { mem::transmute(code) };
59    GenericArray::clone_from_slice(&slice)
60  }))
61}
62
63#[repr(packed)]
64pub struct JumpShort {
65  opcode: u8,
66  operand: i8,
67}
68
69/// Constructs a relative short jump.
70pub fn jmp_rel8(displacement: i8) -> Box<dyn Thunkable> {
71  Box::new(FixedThunk::<typenum::U2>::new(move |_| {
72    let code = JumpShort {
73      opcode: 0xEB,
74      operand: displacement - mem::size_of::<JumpShort>() as i8,
75    };
76
77    let slice: [u8; 2] = unsafe { mem::transmute(code) };
78    GenericArray::clone_from_slice(&slice)
79  }))
80}
81
82/// Calculates the relative displacement for an instruction.
83fn calculate_displacement(source: usize, destination: usize, instruction_size: usize) -> u32 {
84  let displacement =
85    (destination as isize).wrapping_sub(source as isize + instruction_size as isize);
86
87  // Ensure that the detour can be reached with a relative jump (+/- 2GB).
88  // This only needs to be asserted on x64, since it wraps around on x86.
89  #[cfg(target_arch = "x86_64")]
90  assert!(crate::arch::is_within_range(displacement));
91
92  displacement as u32
93}